package inter

import (
	"fmt"
	"showen7/ast"
	"showen7/num"
	"strings"
)

type ObjectType int

const (
	NULL_OBJ ObjectType = iota
	EMPTYLIST_OBJ
	BOOLEAN_OBJ
	NUMBER_OBJ
	STRING_OBJ
	CHARACTER_OBJ
	IDENTIFIER_OBJ
	PROCEDURE_OBJ
	BUILTIN_OBJ
	ERROR_OBJ
	LAMBDA_OBJ
	PAIR_OBJ
	SYMBOL_OBJ
	//LIST_OBJ
	VECTOR_OBJ
	PORT_OBJ
	EOF_OBJ
	PROMISE_OBJ
	MACRO_OBJ
)

// Object represents a value in the Scheme environment
// It's the interface that all Scheme values must implement
type Object interface {
	// Type returns the type of the object
	Type() ObjectType

	// Inspect returns a string representation of the object
	Inspect() string
}

type NullObject struct{}

func (n *NullObject) Type() ObjectType {
	return NULL_OBJ
}

func (n *NullObject) Inspect() string {
	return ""
}

var NULL_OBJECT = &NullObject{}

type EmptyList struct{}

func (n *EmptyList) Type() ObjectType {
	return EMPTYLIST_OBJ
}

func (n *EmptyList) Inspect() string {
	return "()"
}

var EMPTY_LIST = &EmptyList{}

type BooleanObject struct {
	Value bool
}

func (b *BooleanObject) Type() ObjectType {
	return BOOLEAN_OBJ
}

func (b *BooleanObject) Inspect() string {
	if b.Value {
		return "#t"
	}
	return "#f"
}

type NumberObject struct {
	Value num.Number
}

func (i *NumberObject) Type() ObjectType {
	return NUMBER_OBJ
}

func (i *NumberObject) Inspect() string {
	return i.Value.String()
}

type StringObject struct {
	Value string
}

func (s *StringObject) Type() ObjectType {
	return STRING_OBJ
}

func (s *StringObject) Inspect() string {
	// Return the string value quoted
	return fmt.Sprintf("\"%s\"", s.Value)
}

type CharacterObject struct {
	Value rune
}

func (c *CharacterObject) Type() ObjectType {
	return CHARACTER_OBJ
}

func (c *CharacterObject) Inspect() string {
	// Handle special character names
	switch c.Value {
	case ' ':
		return "#\\space"
	case '\n':
		return "#\\newline"
	default:
		return fmt.Sprintf("#\\%c", c.Value)
	}
}

type IdentifierObject struct {
	Value string
}

func (s *IdentifierObject) Type() ObjectType {
	return IDENTIFIER_OBJ
}

func (s *IdentifierObject) Inspect() string {
	return s.Value
}

type SymbolObject struct {
	Value string
}

func (s *SymbolObject) Type() ObjectType {
	return SYMBOL_OBJ
}

func (s *SymbolObject) Inspect() string {
	return s.Value
}

// Lambda represents a user-defined lambda function
type Lambda struct {
	Parameters []string
	Body       ast.Exp
	Env        *Environment
}

// Type returns the object type as a string
func (l *Lambda) Type() ObjectType {
	return LAMBDA_OBJ
}

// Inspect returns the string representation of the lambda
func (l *Lambda) Inspect() string {
	return fmt.Sprintf("(lambda %v %v)", l.Parameters, l.Body.String())
}

// Procedure represents a named procedure in Scheme
type Procedure struct {
	Name   string
	Params []string
	Body   []ast.Exp
	Env    *Environment
}

// Type returns the object type as a string
func (p *Procedure) Type() ObjectType {
	return PROCEDURE_OBJ
}

// String returns the string representation of the procedure
func (p *Procedure) Inspect() string {
	var s strings.Builder

	s.WriteString("(procedure ")
	s.WriteString(p.Name)
	if len(p.Params) > 0 {
		s.WriteString("(")
		for i, param := range p.Params {
			s.WriteString(param)
			if i < len(p.Params)-1 {
				s.WriteString(" ")
			}
		}
		s.WriteString(")")
	}
	s.WriteString(")")
	return s.String()
}

// PairObject represents a pair (cons cell) in Scheme
type PairObject struct {
	Car any
	Cdr any
}

func (p *PairObject) Type() ObjectType {
	return PAIR_OBJ
}

func (p *PairObject) Inspect() string {
	var s strings.Builder
	s.WriteString("(")
	current := p
	first := true
	for {
		if first {
			first = false
		} else {
			s.WriteString(" ")
		}

		// Handle car
		switch car := current.Car.(type) {
		case Object:
			s.WriteString(car.Inspect())
		case num.Number:
			s.WriteString(car.String())
		case bool:
			if car {
				s.WriteString("#t")
			} else {
				s.WriteString("#f")
			}
		default:
			s.WriteString(fmt.Sprintf("%v", car))
		}

		// Handle cdr
		switch cdr := current.Cdr.(type) {
		case *EmptyList:
			// This is the end of a proper list
			break
		case *PairObject:
			// Continue with the next pair
			current = cdr
			continue
		case Object:
			// This is a dotted pair
			s.WriteString(" . ")
			s.WriteString(cdr.Inspect())
		default:
			// This is a dotted pair with a non-Object
			s.WriteString(" . ")
			s.WriteString(fmt.Sprintf("%v", cdr))
		}
		break
	}
	s.WriteString(")")
	return s.String()
}

/*	//if p.Value == nil {
	//	return "()"
	//}

	// Check if this is a proper list
	if isProperList(p.Value) {
		// Format as a list
		elements := make([]string, 0)
		current := p.Value
		for current != nil {
			car := current.Car
			if carObj, ok := car.(Object); ok {
				elements = append(elements, carObj.Inspect())
			} else {
				elements = append(elements, fmt.Sprintf("%v", car))
			}

			if current.Cdr == nil {
				break
			}

			if nextPair, ok := current.Cdr.(*ds.Pair); ok {
				current = nextPair
			} else {
				// Improper list - show the last element
				if cdrObj, ok := current.Cdr.(Object); ok {
					elements = append(elements, ".", cdrObj.Inspect())
				} else {
					elements = append(elements, ".", fmt.Sprintf("%v", current.Cdr))
				}
				break
			}
		}
		return fmt.Sprintf("(%s)", strings.Join(elements, " "))
	}

	// Format as a dotted pair
	car := p.Value.Car
	cdr := p.Value.Cdr

	var carStr, cdrStr string
	if carObj, ok := car.(Object); ok {
		carStr = carObj.Inspect()
	} else {
		carStr = fmt.Sprintf("%v", car)
	}

	if cdrObj, ok := cdr.(Object); ok {
		cdrStr = cdrObj.Inspect()
	} else {
		cdrStr = fmt.Sprintf("%v", cdr)
	}

	return fmt.Sprintf("(%s . %s)", carStr, cdrStr)
}*/

/*
// Helper functions for PairObject

	func Car(p *ds.Pair) any {
		return p.Car
	}

	func Cdr(p *ds.Pair) any {
		return p.Cdr
	}

func isProperList(p *ds.Pair) bool {
	current := p
	for current != nil {
		if current.Cdr == nil {
			return true
		}
		if _, ok := current.Cdr.(*ds.Pair); !ok {
			return current.Cdr == EMPTY_LIST
		}
		current = current.Cdr.(*ds.Pair)
	}
	return true
}
*/
// ListObject represents a list in Scheme
/*
type ListObject struct {
	Elements []Object
}

func (l *ListObject) Type() ObjectType {
	return LIST_OBJ
}

func (l *ListObject) Inspect() string {
	var elements []string
	for _, elem := range l.Elements {
		elements = append(elements, elem.Inspect())
	}
	return fmt.Sprintf("(%s)", strings.Join(elements, " "))
}
*/
// VectorObject represents a vector in Scheme
type VectorObject struct {
	Elements []Object
}

func (v *VectorObject) Type() ObjectType {
	return VECTOR_OBJ
}

func (v *VectorObject) Inspect() string {
	var elements []string
	for _, elem := range v.Elements {
		elements = append(elements, elem.Inspect())
	}
	return fmt.Sprintf("#(%s)", strings.Join(elements, " "))
}

// MacroObject represents a macro binding created by define-syntax
type MacroObject struct {
	// Macro defined by syntax-rules
	Name     string
	Literals []string
	// Clauses: each clause has a pattern and a template
	Clauses []MacroClause
	// DefEnv is the environment where the macro was defined. Identifiers
	// in the template that refer to bindings in this environment should
	// be treated as reference to those definition-time bindings (not renamed).
	DefEnv *Environment
}

func (m *MacroObject) Type() ObjectType {
	return MACRO_OBJ
}

func (m *MacroObject) Inspect() string {
	return "<macro>"
}

type MacroClause struct {
	Pattern  ast.Exp
	Template ast.Exp
}

// PortObject represents an I/O port in Scheme
type PortObject struct {
	File    interface{} // This would be *os.File in a full implementation
	IsOpen  bool
	IsInput bool // true for input ports, false for output ports
}

func (p *PortObject) Type() ObjectType {
	return PORT_OBJ
}

func (p *PortObject) Inspect() string {
	if p.IsInput {
		return "#<input port>"
	}
	return "#<output port>"
}

// EofObject represents an end-of-file object in Scheme
type EofObject struct{}

func (e *EofObject) Type() ObjectType {
	return EOF_OBJ
}

func (e *EofObject) Inspect() string {
	return "#<eof>"
}

func NewEofObject() *EofObject {
	return &EofObject{}
}

var EOF_OBJECT = &EofObject{}

type PromiseObject struct {
	// The expression to be evaluated
	Expression any
	// The environment in which to evaluate the expression
	Env *Environment
	// Whether the promise has been forced yet
	Forced bool
	// The result of forcing the promise (cached)
	Result Object
}

func (p *PromiseObject) Type() ObjectType {
	return PROMISE_OBJ
}

func (p *PromiseObject) Inspect() string {
	if p.Forced {
		return "#<promise (forced)>"
	}
	return "#<promise>"
}

// ContinuationResultError is a special error type used to signal that a continuation has been invoked
type ContinuationResultError struct {
	Result Object
}

func (e *ContinuationResultError) Error() string {
	return "continuation result"
}

// ContinuationObject represents a continuation in Scheme
type ContinuationObject struct {
	// The environment at the time the continuation was captured
	Env *Environment
	// A function that when called will resume execution with the given arguments
	ResumeFunc func([]Object) (Object, error)
}

func (c *ContinuationObject) Type() ObjectType {
	return BUILTIN_OBJ // Make it look like a builtin procedure
}

func (c *ContinuationObject) Inspect() string {
	return "#<continuation>"
}

// Invoke calls the continuation with the given arguments
func (c *ContinuationObject) Invoke(args []Object) (Object, error) {
	// When a continuation is invoked, we signal this by returning a special error
	// that carries the result value
	if len(args) > 0 {
		return nil, &ContinuationResultError{Result: args[0]}
	}
	return nil, &ContinuationResultError{Result: NULL_OBJECT}
}

// For compatibility with builtin functions
func (c *ContinuationObject) Fn(args []Object, env *Environment) Object {
	result, err := c.Invoke(args)
	if err != nil {
		// We return the error as-is so it can be caught by the evaluation engine
		return &StringObject{Value: "Error: " + err.Error()}
	}
	return result
}
